package com.lagou.edu.core;

import com.lagou.edu.annotation.*;
import com.lagou.edu.support.BeanDefinition;
import com.lagou.edu.support.BeanFinder;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;

/*
 * TODO: ioc容器对象
 * @Author: wyj
 * @Date: 2021/11/17 22:36
 **/
public class DefaultListableBeanFactory {
    private static final Logger LOGGER = Logger.getLogger("DefaultListableBeanFactory");

    private static final String base_package = "com.lagou.edu";

    /**
     * Cache of singleton objects: bean name to bean instance.
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /**
     * Cache of singleton factories: bean name to ObjectFactory.
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /**
     * Cache of early singleton objects: bean name to bean instance.
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    /**
     * Names of beans that are currently in creation.
     */
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));


    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    private final List<String> beanDefinitionNames = new ArrayList<>(256);
    /**
     * 所有clazz
     */
    private Set<Class<?>> classes;
    // private final Map<String, String> aliasBeanName = new ConcurrentHashMap<>(256);

    /**
     * TODO: 初始化bean
     *
     * @Author: wyj
     * @Date: 2021/11/17 22:37
     * @return: void
     **/
    public void preInstantiateSingletons() throws Exception {
        //扫描所有class对象
        loadAllClass();
        //封装所有class对象为beanDefinition
        loadAllBeanDefinition();
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
        //遍历所有beanname，生成bean对象
        for (String beanName : beanNames) {
            getBean(beanName);
        }
        Set<Map.Entry<String, Object>> entries = singletonObjects.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

    }

    /**
     * TODO: 扫描指定包路径下的所有类
     *
     * @Author: wyj
     * @Date: 2021/11/17 22:38
     * @return: void
     **/
    public void loadAllClass() throws Exception {
        try {
            classes = BeanFinder.getClasses(base_package);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("findAllClass失败···");
        }
    }

    /**
     * TODO: 将所有class对象包装为BeanDefinition
     *
     * @Author: wyj
     * @Date: 2021/11/17 22:39
     * @return: void
     **/
    public void loadAllBeanDefinition() throws Exception {
        for (Class<?> aClass : classes) {
            BeanDefinition beanDefinition = createBeanDefinition(aClass);
            if (null != beanDefinition) {
                beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
                beanDefinitionNames.add(beanDefinition.getBeanName());
            }
        }
    }


    /**
     * TODO: class-> BeanDefinition
     *
     * @param clazz:
     * @Author: wyj
     * @Date: 2021/11/17 22:40
     * @return: com.lagou.edu.support.BeanDefinition
     **/
    public BeanDefinition createBeanDefinition(Class<?> clazz) {
        BeanDefinition beanDefinition = null;
        String beanName = clazz.getSimpleName();
        Annotation[] clazzAnnotations = clazz.getAnnotations();

        for (Annotation annotation : clazzAnnotations) {
            if (annotation instanceof Service || annotation instanceof Mapper) {
                beanDefinition = new BeanDefinition();
                break;
            }
            Component component = annotation instanceof Component ? ((Component) annotation) : null;
            if (null != component) {
                beanDefinition = new BeanDefinition();
                String value = component.value();
                if (null != value && value.length() != 0) {
                    //k:注入到容器的真实名称，v：class名称
                    // this.aliasBeanName.put(value, beanName);
                    beanName = value;
                }
                break;
            }
        }
        if (null != beanDefinition) {
            beanDefinition.setBeanName(beanName);
            System.out.println("beanName="+beanName);
            Field[] fields = clazz.getFields();
            HashMap<Field, Annotation[]> fieldHashMap = new HashMap<>(fields.length);
            for (Field field : fields) {
                Annotation[] fieldAnnotations = field.getAnnotations();
                if (0 != fieldAnnotations.length) {
                    fieldHashMap.put(field, fieldAnnotations);
                }
            }
            beanDefinition.setClazz(clazz);
            beanDefinition.setClazzAnnotationArr(clazzAnnotations);
            beanDefinition.setFieldAnnotationMap(fieldHashMap);
        }
        return beanDefinition;
    }

    /**
     * TODO: 获取bean
     *
     * @param beanName:
     * @author: wyj
     * @date: 2021/11/17 21:40
     * @return: java.lang.Object
     **/
    public Object getBean(String beanName) throws Exception {
        return doGetBean(beanName);
    }

    /**
     * TODO: 获取bean
     *
     * @param beanName:
     * @Author: wyj
     * @Date: 2021/11/17 22:40
     * @return: java.lang.Object
     **/
    public Object doGetBean(String beanName) throws Exception {
        //将当前bean标记为正在创建中
        this.singletonsCurrentlyInCreation.add(beanName);
        Object beanInstance;
        Object sharedInstance = getSingleton(beanName);
        //该bean已经初始化
        if (null != sharedInstance) {
            beanInstance = sharedInstance;
        } else {
            beanInstance = getSingleton(beanName, () -> createBean(beanName));
        }
        this.singletonsCurrentlyInCreation.remove(beanName);
        return beanInstance;
    }

    /**
     * TODO: 获取单例bean
     *
     * @param beanName beanName
     * @return java.lang.Object
     * @author wuyijie
     * @date 2021/11/17 10:29 上午
     */
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) throws Exception {
        Object singletonObject = this.singletonObjects.get(beanName);
        boolean newSingleton = false;
        if (null == singletonObject) {
            synchronized (this.singletonObjects) {
                singletonObject = this.singletonObjects.get(beanName);
                {
                    if (null == singletonObject) {
                        newSingleton = true;
                        singletonObject = singletonFactory.getObject();
                    }
                }
            }
        }
        if (newSingleton) {
            addSingleton(beanName, singletonObject);
        }
        return singletonObject;
    }

    /**
     * TODO: 获取单例bean
     *
     * @param beanName: beanName
     * @Author: wyj
     * @Date: 2021/11/17 22:40
     * @return: java.lang.Object
     **/
    public Object getSingleton(String beanName) throws Exception {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (null == singletonObject) {
            synchronized (this.singletonObjects) {
                singletonObject = this.singletonObjects.get(beanName);
                {
                    if (null == singletonObject) {
                        singletonObject = earlySingletonObjects.get(beanName);
                        if (null == singletonObject) {
                            ObjectFactory<?> objectFactory = singletonFactories.get(beanName);
                            if (null != objectFactory) {
                                singletonObject = objectFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

    /**
     * TODO: 创建单例bean
     *
     * @param beanName: beanName
     * @Author: wyj
     * @Date: 2021/11/17 22:41
     * @return: java.lang.Object
     **/
    public Object createBean(String beanName) throws Exception {
        if (beanDefinitionMap.containsKey(beanName)) {
            return doCreateBean(beanName);
        }
        throw new Exception("createBean 失败，beanName=" + beanName + "的bean未从容器中找到");
    }

    /**
     * TODO: 创建单例bean
     *
     * @param beanName: beanName
     * @Author: wyj
     * @Date: 2021/11/17 22:41
     * @return: java.lang.Object
     **/
    public Object doCreateBean(String beanName) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //创建bean
        final Object bean = Class.forName(beanDefinition.getClazz().getName()).getConstructor().newInstance();
        //将bean放入到三级缓存中
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
        Object exposedObject = bean;
        //为bean属性赋值
        populateBean(beanName, exposedObject);
        //调用初始化bean，应用其后置处理器BeanPostProcessor。增强。
        exposedObject = initializeBean(beanName, exposedObject);
        return exposedObject;

    }

    /**
     * TODO: 创建早期暴露的bean
     *
     * @param beanName: beanName
     * @param bean:     bean
     * @Author: wyj
     * @Date: 2021/11/17 22:41
     * @return: java.lang.Object
     **/
    protected Object getEarlyBeanReference(String beanName, Object bean) {
        return bean;
    }

    /**
     * TODO: bean属性赋值
     *
     * @param beanName: beanName
     * @param instance: 返回值
     * @Author: wyj
     * @Date: 2021/11/17 22:42
     * @return: void
     **/
    protected void populateBean(String beanName, Object instance) throws Exception {
        autowireByType(beanName, instance);
    }

    /**
     * TODO: 根据类型注入属性
     *
     * @param beanName:
     * @param instance:
     * @Author: wyj
     * @Date: 2021/11/17 22:42
     * @return: void
     **/
    public void autowireByType(String beanName, Object instance) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        try {
            Class<?> clazz = beanDefinition.getClazz();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                Annotation[] annotations = declaredField.getAnnotations();
                String fieldBeanName = null;
                for (Annotation annotation : annotations) {
                    //需要注入这个属性对应的bean
                    if (annotation instanceof Autowired) {
                        declaredField.setAccessible(true);
                        //如果这个属性对应的bean已经初始化且完整
                        fieldBeanName = declaredField.getType().getSimpleName();
                    }
                    if (annotation instanceof Qualifer) {
                        declaredField.setAccessible(true);
                        fieldBeanName = ((Qualifer) annotation).value();
                        break;
                    }
                }
                if (null != fieldBeanName) {
                    Object fieldSingleton = getSingleton(fieldBeanName);
                    if (null != fieldSingleton) {
                        declaredField.set(instance, fieldSingleton);
                    } else {
                        fieldSingleton = createBean(fieldBeanName);
                    }
                    declaredField.set(instance, fieldSingleton);
                }
            }

        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        }

    }


    /**
     * TODO: 将早期暴露的对象添加到三级缓存中
     *
     * @param beanName         beanName
     * @param singletonFactory singletonFactory
     * @author wuyijie
     * @date 2021/11/17 10:29 上午
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
            }
        }
    }

    /**
     * TODO: 将单例bean放入到bean缓存池中
     *
     * @param beanName:
     * @param singletonObject:
     * @Author: wyj
     * @Date: 2021/11/17 22:43
     * @return: void
     **/
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    /**
     * TODO: 初始化bean---增强bean
     *
     * @param beanName:
     * @param bean:
     * @Author: wyj
     * @Date: 2021/11/17 22:43
     * @return: java.lang.Object
     **/
    protected Object initializeBean(String beanName, Object bean) throws Exception {
        Object wrappedBean = bean;
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    /**
     * TODO: 初始化bean后，对bean做增强
     *
     * @param existingBean:
     * @param beanName:
     * @Author: wyj
     * @Date: 2021/11/17 21:33
     * @return: java.lang.Object
     **/
    //org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws Exception {
        for (Annotation annotation : beanDefinitionMap.get(beanName).getClazzAnnotationArr()) {
            // 类上有事务注解
            if (annotation instanceof Transactional) {
                TransactionalBeanPostProcessor transactionalBeanPostProcessor = new TransactionalBeanPostProcessor(this);
                return transactionalBeanPostProcessor.postProcessBeforeInitialization(existingBean, beanName);
            }
        }
        return existingBean;
    }


}
